home *** CD-ROM | disk | FTP | other *** search
/ Best Tools for JAVA / Best Tools for JAVA.iso / JAVA_ALL / IDE / SUBARTIC / SUB_ARCT / INPUT / CLICK_AG.JAV < prev    next >
Encoding:
Text File  |  1996-10-04  |  14.7 KB  |  444 lines

  1.  
  2. package sub_arctic.input;
  3.  
  4. import sub_arctic.lib.interactor;
  5. import sub_arctic.lib.manager;
  6.  
  7. /**
  8.  * A positional dispatch agent responsible for managing press/release, click,
  9.  * and double click interactions.  This agent dispatches under three different
  10.  * input protocols: pressable (which does single presses and releases of a 
  11.  * mouse button), clickable (which is a press and a release of a mouse button),
  12.  * and double_clickable (which is two rapid clicks in a small area).  Note:
  13.  * that if the dispatch of a click is consumed, this resets the internal 
  14.  * finite state controller and precludes delivery of a subsequent click as a 
  15.  * double click.  Similarly, if a press or release is consumed, those events
  16.  * will not form part of a click dispatch.<p>
  17.  *
  18.  * @see sub_arctic.input.pressable
  19.  * @see sub_arctic.input.clickable
  20.  * @see sub_arctic.input.double_clickable
  21.  * @author Scott Hudson
  22.  */
  23. public class click_agent extends dispatch_agent implements click_tracking {
  24.  
  25.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  26.  
  27.   /** Part of protocol for click_tracking which we use to be informed about
  28.    *  button presses or releases that occur and are consumed elsewhere. 
  29.    *  Here we happen to ignore this. */
  30.   public void focus_set_enter(
  31.     event                evt, 
  32.     focus_dispatch_agent agnt, 
  33.     Object               user_info)
  34.     {
  35.     }
  36.  
  37.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  38.  
  39.   /** Part of protocol for click_tracking which we use to be informed about
  40.    *  button presses or releases that occur and are consumed elsewhere. 
  41.    *  Here we happen to ignore this. */
  42.   public void focus_set_exit(
  43.     event                evt, 
  44.     focus_dispatch_agent agnt, 
  45.     Object               user_info)
  46.     {
  47.     }
  48.  
  49.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  50.  
  51.   /** State for finite state controller.  States are:
  52.    *    0  start state, have seen nothing yet.
  53.    *    1  have seen press
  54.    *    2  have seen release
  55.    *    3  have dispatched press and release as a click
  56.    *    4  have seen second press after click
  57.    *    5  have seen second press and release after click
  58.    */
  59.   protected int state;
  60.  
  61.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  62.  
  63.   /** Sequence number of last event processed */
  64.   protected int last_evt_seq;
  65.  
  66.   /** Sequence number of last press or release we monitored */
  67.   protected int last_press_release_anywhere;
  68.  
  69.   /** X location of first press in sequence (in global coordinates).
  70.    *  We use this for proximity tests. */
  71.   protected int first_x;
  72.  
  73.   /** Y location of first press in sequence (in global coordinates).
  74.    *  We use this for proximity tests. */
  75.   protected int first_y;
  76.  
  77.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  78.  
  79.   /** Default constructor */
  80.   public click_agent()
  81.     {
  82.       /* put us in the start state */
  83.       state = 0;
  84.  
  85.       /* reset information about previous event */
  86.       last_evt_seq = -1;
  87.       last_press_release_anywhere = -1;
  88.       first_x = 0xffffffff;
  89.       first_y = 0xffffffff;
  90.  
  91.       /* arrange to monitor all presses and releases */
  92.       manager.click_tracker.add_to_focus(this, null, null);
  93.     }
  94.  
  95.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  96.  
  97.   /** 
  98.    * We are only interested in press and release of the mouse button. 
  99.    * @param event evt the event we are indicating interest or non-interest in.
  100.    */
  101.   public boolean event_is_useful(event evt)
  102.     {
  103.       return evt.id() == event.MOUSE_DOWN || evt.id() == event.MOUSE_UP;
  104.     }
  105.  
  106.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  107.  
  108.   /** Constant indicating how close together (in both x and y) events have
  109.    *  to be to qualify as parts of the same click/double-click. 
  110.    */
  111.   protected static final int CLICK_CLOSE = 5;
  112.  
  113.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  114.  
  115.   /** 
  116.    * Determine if event qualifies as close enough to match first event in
  117.    * a sequence.  We do this by comparing against the previously saved 
  118.    * position of the first event in the sequence.
  119.    * @param event next_event event whose position we are testing
  120.    */
  121.   protected boolean matches_first(event next_event)
  122.     {
  123.       return next_event != null && 
  124.          Math.abs(first_x - next_event.global_x()) < CLICK_CLOSE &&
  125.              Math.abs(first_y - next_event.global_y()) < CLICK_CLOSE;
  126.     }
  127.  
  128.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  129.  
  130.   /** 
  131.    * Track press and release events everywhere that are dispatched to us from 
  132.    * the click_track monitoring agent.  We record the event sequence number 
  133.    * of this event and if we don't see a matching event then we know somebody 
  134.    * else got it first and so we reset our state.  We detect this by noting
  135.    * that we get a new event but the previous one was never processed. <p>
  136.    *
  137.    * @param event  evt       the press or release event.
  138.    * @param Object user_info user_info associated with the even dispatch 
  139.    *                         (ignored here).
  140.    */ 
  141.   public boolean track_click(event evt, Object user_info)
  142.     {
  143.       /* if the previous event was never processed */
  144.       if (last_evt_seq != last_press_release_anywhere)
  145.     {
  146.       /* reset our state */
  147.       state = 0;
  148.     }
  149.  
  150.       /* remember sequence number of this event */
  151.       last_press_release_anywhere = manager.event_seq_num();
  152.  
  153.       return true;
  154.     }
  155.  
  156.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  157.  
  158.   /** 
  159.    * This method makes state transitions for a finite state machine 
  160.    * controlling the interaction.  This is a complicated state machine in 
  161.    * which three kinds of transitions can be made.  Each new event moves 
  162.    * to a new state (via this method).  In addition, if an event actually 
  163.    * causes a dispatch, a transition back to the start state (0) may occur
  164.    * (within dispatch_event).  Finally, in certain states (2 and 5 -- 
  165.    * corresponding to a single release or a completed double-click 
  166.    * respectively) if the event is *not* dispatched the machine is also reset 
  167.    * to the start state.  Since we can't actually tell when an event will not 
  168.    * be dispatched (e.g., we can't tell if the positional policy will call us 
  169.    * again with the same event), this final class of transition is actually 
  170.    * handled when the *next* event arrives.  This corresponds to doing two
  171.    * transitions here. <p>
  172.    * 
  173.    * @param event evt the event potentially causing the transition.
  174.    */
  175.   protected void make_transition(event evt)
  176.     {
  177.       /* do transition based on what state we are in */
  178.       switch(state)
  179.     {
  180.       case 2: /* have seen release */
  181.         /* if we are still in this state, then we didn't dispatch
  182.          * the release and we should really be back in the start
  183.          * state.  So... fall through to that state. 
  184.          */
  185.       case 5: /* have seen press, release, press, release */
  186.         /* if we are still in this state, then we didn't dispatch
  187.          * the double-click and we should really be back in the 
  188.          * start state.  So... fall through to that state.
  189.         */
  190.       case 0: /* start state */
  191.         if (evt.id() == event.MOUSE_DOWN) 
  192.           state = 1;
  193.         else /* MOUSE_UP */
  194.           state = 2;
  195.          
  196.         /* remember position of this event for later comparison */
  197.         first_x = evt.global_x();
  198.         first_y = evt.global_y();
  199.       break;
  200.  
  201.       case 1: /* have seen one press */
  202.         if (evt.id() == event.MOUSE_UP)
  203.           state = 3;
  204.         else /* MOUSE_DOWN */
  205.           state = 1;
  206.       break;
  207.  
  208.  
  209.       case 3: /* have seen both press and release */
  210.         if (evt.id() == event.MOUSE_DOWN)
  211.           {
  212.             /* if there were close enough for a double go that way */
  213.             if (evt.clickCount() >= 2)
  214.               state = 4;
  215.             else
  216.               /* otherwise, we reset and take just the press */
  217.               state = 1;
  218.           }
  219.         else /* MOUSE_UP */
  220.           state = 2;
  221.       break;
  222.          
  223.        case 4: /* have seen press, release, press */
  224.          if (evt.id() == event.MOUSE_UP)
  225.            state = 5;
  226.          else /* MOUSE_UP */
  227.            state = 1;
  228.        break;
  229.  
  230.      }
  231.     }
  232.  
  233.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  234.  
  235.   /**
  236.    * Attempt to perform event dispatch(es) to the given object, resetting
  237.    * the state to the start state when appropriate (see comments for 
  238.    * make_transition).<p.
  239.    *
  240.    * @see sub_arctic.input.click_agent#make_transition
  241.    * @param interactor to_obj    the object we are trying to dispatch to.
  242.    * @param event      evt       the event to pass to the object.
  243.    * @param Object     user_info uninterpreted user info to pass to the object.
  244.    * @return boolean whether the event has been dispatched and consumed.
  245.    */
  246.   protected boolean  do_dispatches(
  247.     interactor       to_obj, 
  248.     event            evt, 
  249.     Object           user_info)
  250.     {
  251.       boolean result   = false;
  252.  
  253.       /* attempt dispatches called for in each state */
  254.       switch (state)
  255.     {
  256.       case 0: /* start state */
  257.         /* no dispatches out of this state */
  258.         result = false;
  259.       break;
  260.  
  261.       case 1: /* have seen press */
  262.       case 4: /* have seen press, release, press */
  263.         /* can dispatch a press, see if object wants it */
  264.         if (to_obj instanceof pressable)
  265.           {
  266.         /* put event into local coords of object and try to dispatch */
  267.         evt.global_to_local(to_obj);
  268.             result = ((pressable)to_obj).press(evt, user_info);
  269.  
  270.         /* if the object took it we reset unless the object also wants
  271.          * the full click or double click 
  272.          */
  273.         if (result && !(to_obj instanceof clickable || 
  274.                         to_obj instanceof double_clickable))
  275.           state = 0;
  276.           }
  277.       break;
  278.  
  279.       case 2: /* have seen release */
  280.         /* can dispatch a release, see if object wants it */
  281.         if (to_obj instanceof pressable)
  282.           {
  283.         /* put event into local coords of object and try to dispatch */
  284.         evt.global_to_local(to_obj);
  285.             result = ((pressable)to_obj).release(evt, user_info);
  286.  
  287.         /* if the object took it we reset unless the object also wants
  288.          * the full click or double click 
  289.          */
  290.         if (result && !(to_obj instanceof clickable || 
  291.                         to_obj instanceof double_clickable))
  292.           state = 0;
  293.           }
  294.       break;
  295.  
  296.       case 3: /* have seen press and release */
  297.         /* can dispatch a release or a click, try both, release first */
  298.         if (to_obj instanceof pressable)
  299.           {
  300.         /* put event into local coords of object and try to dispatch */
  301.         evt.global_to_local(to_obj);
  302.             result = ((pressable)to_obj).release(evt, user_info);
  303.  
  304.         /* if the object took it we reset unless the object also wants
  305.          * the full click or double click 
  306.          */
  307.         if (result && !(to_obj instanceof clickable || 
  308.                         to_obj instanceof double_clickable))
  309.           state = 0;
  310.           }
  311.         /* now try the click */
  312.         if (to_obj instanceof clickable)
  313.           {
  314.         /* put event into local coords of object and try to dispatch */
  315.         evt.global_to_local(to_obj);
  316.         if (((clickable)to_obj).click(evt, user_info))
  317.           {
  318.             result = true;
  319.  
  320.             /* if the object took it we reset unless the object also 
  321.              * wants the double click 
  322.              */
  323.             if (!(to_obj instanceof double_clickable))
  324.               state = 0;
  325.           }
  326.           }
  327.       break;
  328.  
  329.       case 5: /* have seen press, release, press, release */
  330.         /* can dispatch a release, a click, or a double click, try all */
  331.         if (to_obj instanceof pressable)
  332.           {
  333.         /* put event into local coords of object and try to dispatch */
  334.         evt.global_to_local(to_obj);
  335.             result = ((pressable)to_obj).release(evt, user_info);
  336.  
  337.         /* if the object took it we reset unless the object also wants
  338.          * the full click or double click 
  339.          */
  340.         if (result && !(to_obj instanceof clickable || 
  341.                         to_obj instanceof double_clickable))
  342.           state = 0;
  343.           }
  344.         /* now try the click */
  345.         if (to_obj instanceof clickable)
  346.           {
  347.         /* put event into local coords of object and try to dispatch */
  348.         evt.global_to_local(to_obj);
  349.         if (((clickable)to_obj).click(evt, user_info))
  350.           {
  351.             result = true;
  352.  
  353.             /* if the object took it we reset unless the object also 
  354.              * wants the double click 
  355.              */
  356.             if (!(to_obj instanceof double_clickable))
  357.               state = 0;
  358.           }
  359.           }
  360.         /* finally, try the double-click */
  361.         if (to_obj instanceof double_clickable)
  362.           {
  363.         /* put event into local coords of object and try to dispatch */
  364.         evt.global_to_local(to_obj);
  365.         if (((double_clickable)to_obj).double_click(evt, user_info))
  366.           {
  367.             result = true;
  368.             state = 0;
  369.           }
  370.           }
  371.       break;
  372.     }
  373.  
  374.       return result;
  375.     }
  376.  
  377.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  378.  
  379.   /** 
  380.    * Accept an event from the positional policy and attempt to dispatch it to
  381.    * an object.<p>
  382.    *
  383.    * @param event      evt       the event being dispatched.
  384.    * @param Object     user_info uninterpreted information to be passed to 
  385.    *                             the object.
  386.    * @param interactor to_obj    the object we should attempt to dispatch to.
  387.    * @param int        seq_num   the sequence number of this event (so we can
  388.    *                             tell if we have seen this before in a failed
  389.    *                             attempt to dispatch it to another object).
  390.    * @return boolean indicating whether the event was dispatched and consumed.
  391.    */
  392.   public boolean dispatch_event(
  393.     event            evt,
  394.     Object           user_info,
  395.     interactor       to_obj,
  396.     int              seq_num)
  397.     {
  398.       boolean result, do_reset;
  399.  
  400.       /* if this is the first time we have seen this event, make a state 
  401.        * transition on the event */
  402.       if (seq_num != last_evt_seq)
  403.     {
  404.       /* remember this sequence number */
  405.       last_evt_seq = seq_num;
  406.  
  407.       /* reset if we are part way through a sequence and this is now too
  408.        * far away. 
  409.        */
  410.       if (!matches_first(evt))
  411.         state = 0;
  412.             
  413.        /* make a transition on this event */
  414.        make_transition(evt);
  415.     }
  416.  
  417.       /* we are now in the proper state, determine which dispatches we can
  418.        * do from this state and try to do them.
  419.        */
  420.       result = do_dispatches(to_obj, evt, user_info);
  421.  
  422.       /* return the result */
  423.       return result;
  424.     }
  425.  
  426.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  427. }
  428. /*=========================== COPYRIGHT NOTICE ===========================
  429.  
  430. This file is part of the subArctic user interface toolkit.
  431.  
  432. Copyright (c) 1996 Scott Hudson and Ian Smith
  433. All rights reserved.
  434.  
  435. The subArctic system is freely available for most uses under the terms
  436. and conditions described in 
  437.   http://www.cc.gatech.edu/gvu/ui/sub_arctic/sub_arctic/doc/usage.html 
  438. and appearing in full in the lib/interactor.java source file.
  439.  
  440. The current release and additional information about this software can be 
  441. found starting at: http://www.cc.gatech.edu/gvu/ui/sub_arctic/
  442.  
  443. ========================================================================*/
  444.